#include "linux/types.h"
#include "linux/hugetlb.h"
#include "linux/err.h"
#include "linux/errno.h"
#include "linux/page.h"
#include "linux/sched.h"
#include "linux/mm.h"
#include "linux/init.h"
#include "linux/kernel.h"
#include "asm/pgtable.h"

static struct page *no_page_table(struct vm_area_struct *vma, unsigned int flags)
{
	if ((flags & FOLL_DUMP) && (!vma->vm_ops || !vma->vm_ops->fault))
		return ERR_PTR(-EFAULT);

	return NULL;
}

static inline int is_vm_hugetlb_page(struct vm_area_struct *vma)
{
    return 0;
}

static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags)
{
    return 0;
}

/*
 * 处理页错误，将虚拟地址映射到物理页
 * tsk: 当前任务（进程）
 * vma: 虚拟内存区域
 * address: 发生错误的虚拟地址
 * flags: 指向标志位的指针，控制错误处理行为
 * nonblocking: 指向非阻塞标志的指针
 */
static int faultin_page(struct task_struct *tsk, struct vm_area_struct *vma, unsigned long address, unsigned int *flags, int *nonblocking)
{
    unsigned int fault_flags = 0;
    int ret;

    // 检查标志组合：如果只有FOLL_MLOCK而没有FOLL_POPULATE，返回错误
    // FOLL_MLOCK用于锁定页，但需要FOLL_POPULATE来实际填充页
    if ((*flags & (FOLL_POPULATE | FOLL_MLOCK)) == FOLL_MLOCK)
        return -ENOENT;

    // 如果需要写权限，设置写错误标志
    if (*flags & FOLL_WRITE)
        fault_flags |= FAULT_FLAG_WRITE;

    // 如果处理远程内存（如RDMA），设置远程错误标志
    if (*flags & FOLL_REMOTE)
        fault_flags |= FAULT_FLAG_REMOTE;

    // 如果允许非阻塞操作，设置允许重试标志
    if (nonblocking)
        fault_flags |= FAULT_FLAG_ALLOW_RETRY;

    // 如果要求不等待，设置允许重试和立即重试标志
    if (*flags & FOLL_NOWAIT)
        fault_flags |= FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_RETRY_NOWAIT;
    
     // 如果已经尝试过，设置已尝试标志
    if (*flags & FOLL_TRIED)
    {
        //VM_WARN_ON_ONCE(fault_flags & FAULT_FLAG_ALLOW_RETRY);
        fault_flags |= FAULT_FLAG_TRIED;
    }

     // 调用核心的页错误处理函数, 这个函数会实际分配物理页、处理写时复制(COW)等
    ret = handle_mm_fault(tsk->mm, vma, address, fault_flags);
    if (ret & VM_FAULT_ERROR)
    {
        if (ret & VM_FAULT_OOM) // 内存不足错误
            return -ENOMEM;
        if (ret & (VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV))  // 信号错误（总线错误或段错误）
            return -EFAULT;

        return -EFAULT; // 其他错误也返回EFAULT
    }

     // 检查是否需要重试（通常发生在非阻塞情况下）
    if (ret & VM_FAULT_RETRY)
    {
        /*
         * 当前无法立即完成页错误处理,需要释放锁并重试
         * 可能是由于锁竞争、内存压力等原因
         * 避免无限重试(活锁), 清除非阻塞标志
         */
        *nonblocking = 0;
        return -EBUSY; // 返回繁忙，调用者应该重试
    }

    // 检查是否发生了写时复制(COW), 如果错误处理表明发生了写操作，但VMA没有写权限，说明发生了COW
    if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))
        *flags |= FOLL_COW;

    return 0;
}

static struct page *follow_trans_huge_pmd(struct vm_area_struct *vma, unsigned long address, pmd_t *pmd, unsigned int flags)
{
    return NULL;
}

static struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr, pmd_t *pmd, int flags)
{
	return NULL;
}

struct page * follow_page_pte(struct vm_area_struct *vma, unsigned long address, pmd_t *pmd, unsigned int flags)
{
    struct mm_struct *mm = vma->vm_mm;
    struct page *page;
    pte_t *ptep, pte;
	spinlock_t *ptl;

    // 检查PMD是否异常（坏页表）
    if (pmd_bad(*pmd))
        return no_page_table(vma, flags);

    // 获取PTE指针并加锁，保证在查找过程中页表不会被修改
    ptep = pte_offset_map_lock(mm, pmd, address, &ptl);
    pte = *ptep;

    // 检查PTE是否表示页在物理内存中
    if (!pte_present(pte))
    {
        // 当前流程还会处理一些关于页表迁移、交换相关的情况，但是现有代码并不涉及
        goto no_page;
    }

    // 写权限检查：如果需要写权限但没有写权限，返回NULL
    if (flags & FOLL_WRITE && !can_follow_write_pte(pte, flags))
    {
        pte_unmap_unlock(ptep, ptl);
        return NULL;
    }
    
    // 获取正常的物理页映射
    page = vm_normal_page(vma, address, pte);
    
    // 如果没找到正常页，但PTE是设备映射且要求FOLL_GET
    if (!page && pte_devmap(pte) && (flags & FOLL_GET))
    {
        /*
		  只有在FOLL_GET情况下才返回设备映射页，
		  因为它们只在持有pgmap引用时有效
		// 获取设备页映射
		pgmap = get_dev_pagemap(pte_pfn(pte), NULL);
		if (pgmap)
			page = pte_page(pte);  // 获取设备页
		else
			goto no_page;  // 无法获取设备映射，返回无页
		 */
    }
    else if (!page)
    {
        // 核心转储场景
        if (flags & FOLL_DUMP)
        {
            page = ERR_PTR(-EFAULT);
            goto out;
        }

        // 检查是否是零页
        if (is_zero_pfn(pte_pfn(pte)))
        {

        }
        else
        {

        }
    }

    // 透明巨型页相关，当前代码不涉及 !!!!!!!!!!
    if (flags & FOLL_SPLIT)
    {

    }

    // 如果需要获取页引用（FOLL_GET）
    if (flags & FOLL_GET)
    {
        get_page(page);
    }

    // 接触页以更新访问时间、脏页状态等
    if (flags & FOLL_TOUCH)
    {

    }

    // 处理锁定页的情况
    if (flags & FOLL_MLOCK && vma->vm_flags & VM_LOCKED)
    {

    }

out:
	pte_unmap_unlock(ptep, ptl); // 释放PTE锁
    return page; // 返回找到的页
no_page:
	pte_unmap_unlock(ptep, ptl);
    return NULL;
}

/*
 * 根据虚拟地址查找对应的物理页
 * vma: 虚拟内存区域描述符
 * address: 要查找的虚拟地址
 * flags: 控制查找行为的标志（如 FOLL_WRITE、FOLL_GET 等）
 * page_mask: 输出参数，返回页大小相关信息
 *
 * 返回映射的(struct page *)，如果不存在映射则返回NULL，或者
 * 如果映射到不由页描述符表示的内容则返回错误指针（参见vm_normal_page()）。
 */
struct page *follow_page_mask(struct vm_area_struct *vma, unsigned long address, unsigned int flags, unsigned int *page_mask)
{
    pgd_t *pgd;
	pmd_t *pmd;
    struct page *page;
    spinlock_t *ptl;
    struct mm_struct *mm = vma->vm_mm;

    // 初始化输出参数
    *page_mask = 0;

    pgd = pgd_offset(mm, address);
    if (pgd_none(*pgd) || pgd_bad(*pgd))
        return no_page_table(vma, flags);

    pmd = pmd_offset(pgd, address);
    if (pmd_none(*pmd))
        return no_page_table(vma, flags);

    // 检查是否是巨型PMD页且VMA支持hugetlbfs
    if (pmd_huge(*pmd) && vma->vm_flags &VM_HUGEPAGE)
    {
        // 处理巨型PMD页, 当前代码不涉及到huge page !!!!!!!!!
        page = follow_huge_pmd(mm, address, pmd, flags);
        if (page)
            return page;
        return no_page_table(vma, flags); // 无页表或坏页表
    }

    //处理NUMA相关，当前代码暂不涉及 !!!!!!!!!!
    if (flags & FOLL_NUMA && pmd_protnone(*pmd))
        return no_page_table(vma, flags);

    // 检查是否是设备映射PMD
    if (pmd_devmap(*pmd))
    {
        ptl = pmd_lock(mm, pmd); // 获取PMD锁
        page = follow_devmap_pmd(vma, address, pmd, flags); // 处理设备映射PMD
        spin_unlock(ptl); // 释放锁
        if (page)
            return page;
    }

    // 检查是否不是巨型页(常见情况)
    if (!pmd_trans_huge(*pmd))
        return follow_page_pte(vma, address, pmd, flags); // 处理常规的PTE级别页表

    // 以下是透明巨型页(THP)的处理路径,当前代码暂不涉及 !!!!!!!!!!
    //
    // ...
    //
    page = follow_trans_huge_pmd(vma, address, pmd, flags);

    return page;
}

static long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
		                     unsigned long start, unsigned long nr_pages,
		                     unsigned int gup_flags, struct page **pages,
		                     struct vm_area_struct **vmas, int *nonblocking)
{
    long i = 0;
    unsigned int page_mask;
    unsigned int foll_flags = gup_flags;
    struct vm_area_struct *vma = NULL;

    if (!nr_pages)
        return 0;

    do 
    {
        int ret;
        struct page *page;
        unsigned int page_incream;

        // 第一次迭代或跨越VMA边界时
        if (!vma || start >= vma->vm_end)
        {
            // 查找包含start地址的VMA，必要时扩展VMA
            vma = find_extend_vma(mm, start);
            if (!vma && in_gate_area(mm, start))
            {

            }

            if (!vma || check_vma_flags(vma, gup_flags))
                return i ? i : -EFAULT;
            
            if (is_vm_hugetlb_page(vma))
            {
                continue;
            }
        }
retry:
        /*
        // 检查是否有致命信号 pending
        if (unlikely(fatal_signal_pending(current)))
            return i ? i : -ERESTARTSYS;
        cond_resched();  // 主动调度，避免长时间占用CPU
        */
  
        page = follow_page_mask(vma, start, gup_flags, &page_mask);
        if (!page) // 页面不在内存中或需要处理缺页
        {
            // 触发缺页处理
            ret = faultin_page(tsk, vma, start, &foll_flags, nonblocking);
            switch (ret)
            {
            case 0:  // 缺页处理成功，重试获取页面
                goto retry;
            case -EFAULT: // 权限错误或无效地址
            case -ENOMEM: // 内存不足
            case -EHWPOISON: // 硬件内存错误
                return i ? i : ret; // 返回已处理数或错误
            case -EBUSY: // 页面被锁定，稍后重试
                return i;
            case -ENOENT: // 页面不存在但可以继续
                goto next_page;
            }
            BUG();
        }
        else if (PTR_ERR(page) == -EEXIST)
        {
			// 页表项存在但没有对应的struct page结构, 通常发生在特殊映射情况
            goto next_page;
        }
        else if (IS_ERR(page)) // 其他错误
        {
            return i ? i: PTR_ERR(page);
        }
        
        // 如果调用者要求返回page指针数组
        if (pages)
        {
            pages[i] = page;
            //flush_anon_page(vma, page, start);
			//flush_dcache_page(page);
			page_mask = 0;
        }
next_page:
        // 如果调用者要求返回VMA数组
        if (vmas)
        {
            vmas[i] = vma;
            page_mask = 0;
        }

        // 计算本次迭代实际处理的页面数
        page_incream = 1 + (~(start >> PAGE_SHIFT) & page_mask);
        if (page_incream > nr_pages)
            page_incream = nr_pages;
        
        // 更新计数器和地址
        i += page_incream;
        start += page_incream * PAGE_SIZE;
        nr_pages -= page_incream;
    
    } while (nr_pages); // 继续直到处理完所有请求页面

    return i;
}


long populate_vma_page_range(struct vm_area_struct *vma, unsigned long start, unsigned long end, int *locked)
{
    struct mm_struct *mm = vma->vm_mm;
    unsigned long nr_pages = (end - start) / PAGE_SIZE;
    int gup_flags = 0;
    //unsigned long addr;
    //int ret;

    return __get_user_pages(current, mm, start, nr_pages, gup_flags, NULL, NULL, locked);

}

/*
 * 预分配物理页面，建立虚拟地址到物理页面的映射
 * start：起始虚拟地址（必须页对齐）
 * len：区域长度（必须页对齐）
 * ignore_errors：是否忽略错误继续执行
 */
int __mm_populate(unsigned long start, unsigned long len, int ignore_errors)
{
    struct mm_struct *mm = current->mm;
    unsigned long end, nstart, nend;
    struct vm_area_struct *vma;
    int locked = 0;  // 标记是否已获取内存映射锁
    long ret = 0;

    // 确保地址页对齐
    //VM_BUG_ON(start & ~PAGE_MASK);
    //VM_BUG_ON(len != PAGE_ALIGN(len));

    end = start + len; // 计算结束地址

    // 每次处理一个 VMA 或 VMA 的一部分,可以处理跨越多个 VMA 的大内存区域
    for (nstart = start; nstart < end; nstart = nend)
    {
        if (!locked)
        {
            locked = 1;
            //down_read(&mm->mmap_sem);
            vma = find_vma(mm, nstart); // 找到包含 nstart 的 VMA
        }
        else if (nstart >= vma->vm_end) // 当前地址超出当前 VMA 范围时，移动到下一个 VMA
            vma = vma->vm_next; 
        
        if (!vma || vma->vm_start >= end) 
            break;
        
        nend = min(end, vma->vm_end); // nend 取目标结束地址和 VMA 结束地址的较小值
        if (vma->vm_flags & (VM_IO | VM_PFNMAP)) // I/O 映射区域和特殊物理页映射， 不分配物理页
            continue;

        /*
         *  确保 nstart 不小于 VMA 起始地址
         *  例：
         *  1 ：正常情况（不需要调整）
         *  start = 0x5000, vma->vm_start = 0x5000
         *  → nstart = 0x5000 (保持不变)
         * 
         *  2：需要调整的情况
         *  start = 0x4000, vma->vm_start = 0x5000  
         *  → nstart = 0x5000 (调整到VMA起始位置)
         *  
         *  3：跨越多个VMA
         *  第一个VMA: [0x1000, 0x3000)
         *  第二个VMA: [0x5000, 0x7000)
         *  要populate的范围: [0x2000, 0x6000)
         *  
         *  处理第一个VMA时：
         *  nstart = 0x2000, vma->vm_start = 0x1000 → 不需要调整
         *  nend = min(0x6000, 0x3000) = 0x3000
         *  处理第二个VMA时：
         *  nstart = 0x3000 (上一轮结束), vma->vm_start = 0x5000
         *  0x3000 < 0x5000 → 调整nstart到0x5000
         * 
         *  处理完之后0x3000-0x5000的区域为空洞，因为它不在任何VMA内
         */ 
        if (nstart < vma->vm_start)
            nstart = vma->vm_start;
        
        // 执行实际的页面分配和映射
        ret = populate_vma_page_range(vma, nstart, nend, &locked);
        if (ret < 0)
        {
            // 如果 ignore_errors 为真，忽略错误继续处理下一个 VMA
            if (ignore_errors)
            {
                ret = 0;
                continue;
            }
        
            // 否则立即退出
            break;
        }

        nend = nstart + ret * PAGE_SIZE; // 根据分配的页面数更新 nend

        ret = 0;
    }

    // 如果持有锁则释放
    if (locked)
    {
        locked = 0; 
        //up_read(&mm->mmap_sem);
    }

    return 0;
}

void mm_populate(unsigned long addr, unsigned long len)
{
	(void) __mm_populate(addr, len, 1);
}